Skip to content

RenderedBehaviourExtension ​

RenderedBehaviourExtension lets a behaviour contribute rendering output alongside the block entity it is attached to. Two rendering paths are available: a standard Block Entity Renderer (BER) that always works, and an optional Flywheel visual for hardware-accelerated rendering.

For an overview of all extensions, see Super Block Entity Behaviours.

Implementing ​

To use a renderer, it is suggested you create a public static final supplier instance, which functions as a singleton for the renderer factory. This is not strictly required, but it is more efficient to reuse the same supplier instance across all behaviour instances than to create a new one for each:

java
public class MyRenderedBehaviour extends SuperBlockEntityBehaviour
        implements RenderedBehaviourExtension {

    public static final BehaviourType<MyRenderedBehaviour> TYPE = new BehaviourType<>();
    public static final BehaviourRenderSupplier RENDERER = () -> MyBehaviourRenderer::new;

    public MyRenderedBehaviour(final SmartBlockEntity be) {
        super(be);
    }

    @Override
    public BehaviourType<?> getType() {
        return TYPE;
    }

    @Override
    public BehaviourRenderSupplier getRenderer() {
        return RENDERER;
    }
}

Block Entity Renderer (BER) ​

getRenderer() ​

Required. Returns a BehaviourRenderSupplier, which produces a new BlockEntityBehaviourRenderer instance when Azimuth needs one.

java
public class MyBehaviourRenderer extends BlockEntityBehaviourRenderer<MyBlockEntity> {
    @Override
    public void renderSafe(final SuperBlockEntityBehaviour behaviour, final MyBlockEntity blockEntity,
                           final float partialTicks, final PoseStack ms, final MultiBufferSource buffer,
                           final int light, final int overlay) {
        // render here
    }
}

The type parameter T controls the type of the blockEntity argument. If the cast fails at runtime, Azimuth throws an informative ClassCastException.

rendersWhenVisualizationAvailable() ​

Controls whether the BER still runs when a Flywheel visual is active for the same block entity. The default is true. Return false when the visual fully replaces the BER output.

getRenderBoundingBox() ​

Optional. Return an AABB to expand the culling bounds for the owning block entity. Returning null leaves the bounds unchanged.

If a RenderedBehaviourExtension is attached after the block entity has already been rendered, call ((CachedRenderBBBlockEntity) blockEntity).invalidateRenderBoundingBox() to force a client-side refresh.

Flywheel Visual Interest ​

Flywheel visuals are injected through a wrapping visualizer. Register interest only for the block entity types that might actually create a behaviour visual:

java
public static void register() {
    VisualWrapperInterest.registerInterest(type ->
        type == MyBlockEntityType.MY_TYPE.get()
    );
}

private static void commonSetup(final FMLCommonSetupEvent event) {
    VisualWrapperInterest.resolve();
}

Flywheel Visual ​

getVisualFactory() ​

Optional. Return a BehaviourVisualFactory to attach a Flywheel visual to this behaviour. Return null to skip the visual path entirely.

java
@Override
public BehaviourVisualFactory getVisualFactory() {
    return (context, behaviour, blockEntity, parentVisual, partialTick) -> {
        if (!(blockEntity instanceof final MyKineticBlockEntity kineticBlockEntity) || behaviour != this) {
            return null;
        }
        return new MyBehaviourVisual(parentVisual);
    };
}

That guard pattern is useful when only some block entities attached to a behaviour can actually build the visual.

Implementing BehaviourVisual ​

Extend RenderedBehaviourExtension.BehaviourVisual and implement delete(). The other lifecycle hooks are optional:

java
public class MyBehaviourVisual extends RenderedBehaviourExtension.BehaviourVisual {

    // Flywheel instances here

    public MyBehaviourVisual(final AbstractBlockEntityVisual<?> parentVisual) {
        super(parentVisual);
    }

    @Override
    public void update(final float partialTick) {
        // update instance transforms
    }

    @Override
    public void updateLight(final float partialTick) {
        // update instance lighting
    }

    @Override
    public void collectCrumblingInstances(final Consumer<Instance> consumer) {
        // feed instances into the block-breaking animation
    }

    @Override
    public void delete() {
        // delete all instances
    }
}

Use getVisualPosition() from BehaviourVisual to place instances at the owning block entity's visual position.